/**********************************************************************************************************************
 * @file    ymodem.h
 *
 * Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @attention
 *
 * @usage
 *
 *********************************************************************************************************************/
#ifndef __YMODEM_H
#define __YMODEM_H

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

#define PACKET_START_INDEX      (0)
#define PACKET_SEQNO_INDEX      (1)
#define PACKET_SEQNO_COMP_INDEX (2)

#define PACKET_HEADER           (3)     /* start, block, block-complement */
#define PACKET_TRAILER          (2)     /* CRC bytes */
#define PACKET_OVERHEAD         (PACKET_HEADER + PACKET_TRAILER)
#define PACKET_SIZE             (128)
#define PACKET_1K_SIZE          (1024)
#define PACKET_TIMEOUT_MS       (3000)

#define FILE_NAME_LENGTH        (64)

/* ASCII control codes: */
enum ymodem_code {
    YMODEM_CODE_NUL = 0x00,
    YMODEM_CODE_SOH = 0x01,     /* start of 128-byte data packet */
    YMODEM_CODE_STX = 0x02,     /* start of 1024-byte data packet */
    YMODEM_CODE_EOT = 0x04,     /* end of transmission */
    YMODEM_CODE_ACK = 0x06,     /* receive OK */
    YMODEM_CODE_NAK = 0x15,     /* receiver error; retry */
    YMODEM_CODE_CAN = 0x18,     /* two of these in succession aborts transfer */
    YMODEM_CODE_REQ = 0x43,     /* use in place of first NAK for CRC mode */
};

/* transmission node */
enum ymodem_mode {
    YMODEM_MODE_RECV = 0x00,    /* receive mode */
    YMODEM_MODE_SEND = 0x01,    /* send mode */
};

enum ymodem_stage {
    YMODEM_STAGE_NONE,
    /* set when C is send */
    YMODEM_STAGE_ESTABLISHING,
    /* set when we've got the packet 0 and sent ACK and second C */
    YMODEM_STAGE_ESTABLISHED,
    /* set when the sender respond to our second C and recviever got a real
     * data packet. */
    YMODEM_STAGE_TRANSMITTING,
    /* set when the sender send a EOT */
    YMODEM_STAGE_FINISHING,
    /* set when transmission is really finished, i.e., after the NAK, C, final
     * NULL packet stuff. */
    YMODEM_STAGE_FINISHED,
};

struct ymodem {
    enum ymodem_mode mode;

    enum ymodem_stage stage;

    char file_name[FILE_NAME_LENGTH];   /* 文件名字 */
    unsigned int file_size;             /* 文件大小 */
    unsigned char *buffer;              /* 数据缓存 */
    unsigned int rd_len;                /* 数据大小 */
    unsigned int timeout;               /* 超时时间，单位毫秒 */
    unsigned int timestart;             /* 起始时间，单位毫秒 */
    unsigned char sn_seq;               /* 数据序号 */
    unsigned int  sn_len;               /* 数据大小 */    

    unsigned int (*time_msec)(void);    /* 获取时间，单位毫秒 */

    int (*read)(void *data, int len);   /* 接收数据函数 */
    int (*write)(void *data, int len);  /* 发送数据函数 */

    int (*begin_packet_cb)(unsigned char *buf, unsigned int len, struct ymodem *ymodem);
    int (*data_packet_cb)(unsigned char *buf, unsigned int len, struct ymodem *ymodem);
    int (*end_packet_cb)(unsigned char *buf, unsigned int len, struct ymodem *ymodem);
};

/**
 * @brief           YMODEM protocol process.
 *
 * @param[in]       ymodem.
 *
 * @return 0 on success
 * @return -EBUSY means on execute
 * @return Anything else on failure
 */
int ymodem_process(struct ymodem *ymodem);
const char *ymodem_u32_to_str(unsigned int val);

#ifdef __cplusplus
}
#endif

#endif /* __YMODEM_H */
